package com.ruoyi.activity.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.activity.mapper.ActivityMapper;
import com.ruoyi.activity.mapper.CommentMapper;
import com.ruoyi.activity.mapper.TemplateMapper;
import com.ruoyi.activity.service.IActivityService;
import com.ruoyi.common.constant.ActivityTypeConstants;
import com.ruoyi.common.constant.ActivitysConstant;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.BeanCopyUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.model.activity.Activity;
import com.ruoyi.model.activity.dto.ActivityDto;
import com.ruoyi.model.activity.vo.ActivityAndCommonVo;
import com.ruoyi.model.activity.vo.ActivityListVo;
import com.ruoyi.model.auth.AuthContextHolder;
import com.ruoyi.model.comment.Comment;
import com.ruoyi.model.contest.Contest;
import com.ruoyi.model.contest.vo.ContestListVo;
import com.ruoyi.model.template.Template;
import com.ruoyi.model.template.TemplateValue;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : 张怀秋
 **/
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity> implements IActivityService {

    /**
     * 活动服务
     */
    private final ActivityMapper activityMapper;

    /**
     * 密码服务
     */
    @Resource
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    /**
     * 模板服务
     */
    private final TemplateMapper templateMapper;

    /**
     * 评论服务
     */
    private final CommentMapper commentMapper;

    @Resource
    private RedisCache redisCache;

    /**
     * 发布活动接口
     *
     * @param activityDto 前端编辑的活动数据
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean pushActivity(ActivityDto activityDto) {

        //1.为了防止恶意用户Xss攻击 所以这里需要合法用户再次输入自身密码账户密码进行验证
        if (!bCryptPasswordEncoder.matches(activityDto.getPassword(), AuthContextHolder.getUserInfo().getPassword())) {
            //不通过 直接抛出异常
            throw new RuntimeException("您的密码错误!");
        }

        //2. 在service校验参数 方便抛出异常
        checkValue(activityDto);

        //3. 对图片的路径和名称进行处理
//        contestDto.getImgNameList().forEach(name -> {
//            //TODO 后续将这些图片遍历存入图片表中
//        });
//        contestDto.getImgUrlList().forEach(url -> {
//            //TODO 后续将这些图片遍历存入图片表中
//        });

        //4.对象拷贝 将活动信息DTO拷贝进入实体类
        Activity activity = BeanCopyUtils.copyBean(activityDto, Activity.class);
        activity.setPushId(AuthContextHolder.getUserId()); //将当前用户id赋值给发布者id
        //如果不设置是否为团队参赛直接默认为不是团队赛
        if (StringUtils.isEmpty(activity.getIsTeam())) {
            activity.setIsTeam("1"); //默认为不是团队参赛
        } else {
            activity.setTeamNum(activityDto.getTeamNum()); //设置参赛人数
        }
        activity.setParticipationNum(0L);
        activity.setViewNum(0);
        activity.setStatus("1"); //默认待审核状态
        activity.setIsTop("1");
        activity.setSort(-1); //代表没有设置
        activity.setPushTime(new Date()); //当前时间就是发布时间

        //顺便将发布者的姓名也存入数据库当中 这样可以防止很多麻烦的联表
        activity.setPushName(AuthContextHolder.getUserInfo().getName());

        //5. 保存数据库 交给后台进行审核
        boolean result = this.save(activity);

        //6. 如果发布者使用系统的报名模板那么需要把模板存入模板
        List<TemplateValue> teValue = activityDto.getTemplateValueList();
        String templateJson = JSONObject.toJSONString(teValue);

        //7.存入模板表中
        Template template = new Template();
        template.setTemplate(templateJson);
        template.setActivityId(activity.getId());
        template.setType(ActivityTypeConstants.ACTIVITY_TYPE);

        int eff = this.templateMapper.insert(template);

        return result && eff != 0;
    }


    /**
     * 校验参数是否合法
     *
     * @param activityDto 用户发布的活动表单
     */
    public static void checkValue(ActivityDto activityDto) {

        if (StringUtils.isEmpty(activityDto.getContent())) {
            throw new RuntimeException("活动详情不能为空!");
        }

        if (StringUtils.isEmpty(activityDto.getTitle())) {
            throw new RuntimeException("活动标题不能为空!");
        }

        if (StringUtils.isEmpty(activityDto.getOrganizer())) {
            throw new RuntimeException("主办方信息不能为空!");
        }

        if (StringUtils.isEmpty(activityDto.getCategoryId())) {
            throw new RuntimeException("活动分类不能为空!");
        }

        if (StringUtils.isEmpty(activityDto.getLocal())) {
            throw new RuntimeException("活动地址不能为空!");
        }

//        Assert.notNull(contestDto.getLocal(), "活动地址不能为空");

//        if (CollectionUtils.isEmpty(contestDto.getImgNameList())) {
//            throw new RuntimeException("图片名称不能为空!");
//        }
//
//        if (CollectionUtils.isEmpty(contestDto.getImgUrlList())) {
//            throw new RuntimeException("图片地址不能为空!");
//        }

        if (StringUtils.isEmpty(activityDto.getIsEmail())) {
            throw new RuntimeException("必须说明是否可以提交电子档案!");
        }

        if (StringUtils.isEmpty(activityDto.getIsToll())) {
            throw new RuntimeException("是否需要收费不能为空!");
        }

        if (activityDto.getStartDate() == null || activityDto.getEndDate() == null) {
            throw new RuntimeException("活动开始时间和结束时间不明确!");
        }

        if (activityDto.getEnrollStartDate() == null || activityDto.getEnrollEndDate() == null) {
            throw new RuntimeException("活动报名开始时间和结束时间不明确!");
        }

        if (StringUtils.isEmpty(activityDto.getIsComment())) {
            throw new RuntimeException("必须说明是否可以对活动进行评论!");
        }

        //校验收费是否为空
        if (activityDto.getIsToll().equals("0")) { //收费
            BigDecimal toll = activityDto.getToll();
            if (ObjectUtils.isEmpty(toll)) {
                throw new RuntimeException("需要收费的活动的费用不能为空!");
            }
        }

    }

    /**
     * 返回活动详情和对应的评论列表
     *
     * @param activityId 活动id
     * @param type       类型
     * @return ActivityAndCommon
     */
    @Override
    public ActivityAndCommonVo getActivityInfoAndCommentList(String activityId, String type) {
        //1.根据活动id查询活动详情
        Activity activity = this.baseMapper.selectById(activityId);
        //使用封装的对象拷贝工具
        ActivityAndCommonVo activityAndCommonVo = BeanCopyUtils.copyBean(activity, ActivityAndCommonVo.class);
        //2.查询评论列表
        List<Comment> comments = commentMapper.selectList(Wrappers
                .<Comment>lambdaQuery()
                .eq(Comment::getActivityId, activityId)
                .eq(Comment::getType, type));
        //3.返回的是一个平铺的有关该活动的评论列表 需要准备成树形结构
        Map<String, List<Comment>> collect = comments.stream().collect(Collectors.groupingBy(Comment::getToCommentId));
        //4.封装一个递归的方法
        List<Comment> resultCommentList = buildCommentTree(collect, "0");
        activityAndCommonVo.setCommentList(resultCommentList);
        //5.新增浏览量
        redisCache.incrementCacheMapValue(ActivitysConstant.ACTIVITY_KEY, activityId, 1);
        return activityAndCommonVo;
    }


    /**
     * 构建评论列表
     *
     * @param collect 根据所回复的Id分组的平铺数据
     * @param pId     父id
     * @return List<Comment>
     */
    private List<Comment> buildCommentTree(Map<String, List<Comment>> collect, String pId) {
        return collect.getOrDefault(pId, new ArrayList<>()).stream().map(comment ->
                comment.setChildren(buildCommentTree(collect, comment.getId()))
        ).collect(Collectors.toList());
    }

    /**
     * 查询活动
     *
     * @param id 活动主键
     * @return 活动
     */
    @Override
    public Activity selectActivityById(String id) {
        return activityMapper.selectActivityById(id);
    }

    /**
     * 查询活动列表
     *
     * @param activity 活动
     * @return 活动
     */
    @Override
    public List<Activity> selectActivityList(Activity activity) {
        return activityMapper.selectActivityList(activity);
    }

    /**
     * 新增活动
     *
     * @param activity 活动
     * @return 结果
     */
    @Override
    public int insertActivity(Activity activity) {
        return activityMapper.insertActivity(activity);
    }

    /**
     * 修改活动
     *
     * @param activity 活动
     * @return 结果
     */
    @Override
    public int updateActivity(Activity activity) {
        return activityMapper.updateActivity(activity);
    }

    /**
     * 批量删除活动
     *
     * @param ids 需要删除的活动主键
     * @return 结果
     */
    @Override
    public int deleteActivityByIds(String[] ids) {
        return activityMapper.deleteActivityByIds(ids);
    }

    /**
     * 删除活动信息
     *
     * @param id 活动主键
     * @return 结果
     */
    @Override
    public int deleteActivityById(String id) {
        return activityMapper.deleteActivityById(id);
    }

    /**
     * 审核活动
     *
     * @param activityId 活动id
     * @param status     审核结束状态
     * @return 是否审核成功
     */
    @Override
    @Transactional
    public boolean checkActivityStatus(String activityId, String status) {
        //1.查询数据库 找到对应的竞赛
        Activity activity = this.baseMapper.selectById(activityId);
        //2.将状态改成后台发送的状态
        activity.setStatus(status);
        //3.保存进入数据库
        return this.baseMapper.updateById(activity) != 0;
    }

    /**
     * 获取首页中的活动数据 查询热度最高的8条
     *
     * @return List
     */
    @Override
    public  List<Activity> queryIndexActivity() {
        return this.baseMapper.selectIndexActivityList();
    }

    /**
     * @param page       当前页码
     * @param limit      ，每页显示条数
     * @param categoryId 分类id
     * @return List<ContestListVo>
     */
    @Override
    public Map<String, Object> deskActivityList(Long page, Long limit, String categoryId) {
        Page<Activity> activityPage = this.baseMapper.selectPage(new Page<>(page, limit),
                Wrappers.<Activity>lambdaQuery()
                        .eq(Activity::getStatus, "0")
                        .eq(StringUtils.isNotEmpty(categoryId), Activity::getCategoryId, categoryId));

        List<Activity> activityList = activityPage.getRecords().stream().filter(item -> {
            int result = DateUtils.differentDaysByMillisecond(item.getEnrollStartDate(), item.getEnrollEndDate());
            return result > 0 || result == 0;
        }).collect(Collectors.toList());

        List<ActivityListVo> activityListVos = BeanCopyUtils.copyBeanList(activityList, ActivityListVo.class);

        activityListVos.forEach(item -> item.setPour(DateUtils.differentDaysByMillisecond(item.getEnrollStartDate(),
                item.getEnrollEndDate())));

        Map<String, Object> map = new HashMap<>();
        map.put("total", activityList.size());
        map.put("rows", activityListVos);
        return map;
    }

}
